using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
using System.Windows;

namespace Seems.SL.VM
{
	public class BasicVM : DependencyObject, INotifyPropertyChanged 
	{
		private readonly Dictionary<object, object> actions = new Dictionary<object, object>();

		protected DelegateCommand<T> MakeCommand<T>(Action<T> action)
		{
			object result;
			if (actions.TryGetValue(action, out result))
				return (DelegateCommand<T>) result;
			var cmd = new DelegateCommand<T>(action);
			actions.Add(action, cmd);
			return cmd;
		}

		protected DelegateCommand MakeCommand(Action action)
		{
			object result;
			if (actions.TryGetValue(action, out result))
				return (DelegateCommand) result;
			var cmd = new DelegateCommand(action);
			actions.Add(action, cmd);
			return cmd;
		}

		protected virtual void OnPropertyChanged(string propertyName)
		{
			VerifyPropertyName(propertyName);
			var h = PropertyChanged;
			if (h != null)
				h(this, new PropertyChangedEventArgs(propertyName));
		}
		protected virtual void OnPropertyChanging(string propertyName)
		{
			VerifyPropertyName(propertyName);
		}
		public event PropertyChangedEventHandler PropertyChanged;

		public void NotifyOfPropertyChange<TProperty>(Expression<Func<TProperty>> property)
		{
			var lambda = (LambdaExpression)property;
			MemberExpression memberExpression;
			if (lambda.Body is UnaryExpression)
			{
				var unaryExpression = (UnaryExpression)lambda.Body;
				memberExpression = (MemberExpression)unaryExpression.Operand;
			}
			else 
				memberExpression = (MemberExpression)lambda.Body;
			OnPropertyChanged(memberExpression.Member.Name);
		}

		public void NotifyOfPropertyChanging<TProperty>(Expression<Func<TProperty>> property)
		{
			var lambda = (LambdaExpression)property;
			MemberExpression memberExpression;
			if (lambda.Body is UnaryExpression)
			{
				var unaryExpression = (UnaryExpression)lambda.Body;
				memberExpression = (MemberExpression)unaryExpression.Operand;
			}
			else 
				memberExpression = (MemberExpression)lambda.Body;
			OnPropertyChanging(memberExpression.Member.Name);
		}

		void VerifyPropertyName(string propertyName)
		{
			if (GetType().GetProperty(propertyName, BindingFlags.Public) != null)
				throw new ArgumentException(String.Format("Type {0} does not have property {1}", this, propertyName));
		}
		private static bool? _isInDesignMode;
		public static bool IsInDesignModeStatic
		{
			get
			{
				if (!_isInDesignMode.HasValue)
				{
					_isInDesignMode = DesignerProperties.IsInDesignTool;
				}

				return _isInDesignMode.Value;
			}
		}

		public bool IsInDesignMode
		{
			get
			{
				return IsInDesignModeStatic;
			}
		}
	}
}